#!/usr/bin/env python
# coding: utf-8

"""
Created on 17/10/2011

@author: Rubén López
"""

import wx
import os.path
from compiler import DefinitionToRegex

class EditorFrame(wx.Frame):
    """Base Class for the alphabet editor and the regular definitions 
    editor."""
    
    def __init__(self, filename="foo", extension=".bar"):
        super(EditorFrame, self).__init__(wx.GetApp().TopWindow, 
                                          size=(400, 200))
        self.extension = "." + extension
        self.filename = filename + self.extension
        self.wildcard = "*" + self.extension
        self.dirname = "."
        self.text_control = None # Holds the edited text.
        self.CreateInteriorWindowComponents()
        self.CreateExteriorWindowComponents()
        self.Bind(wx.EVT_CLOSE, self.OnExit)

    def CreateInteriorWindowComponents(self):
        """Create "interior" window components. In this case it is just a
        simple multiline text control.
        """
        
        return

    def CreateExteriorWindowComponents(self):
        """Create "exterior" window components, such as menu and status
        bar.
        """
        
        self.CreateMenuBar()
        self.CreateStatusBar()
        self.SetTitle()

    def CreateMenuBar(self):
        """Creates the menu bar with one menu with the options to display
        the about window, open and save files, an exit.
        """
        
        menu = wx.Menu()
        for _id, label, helpText, handler in \
            [(wx.ID_OPEN, "&Open", "Open an existing document", self.OnOpen),
             (wx.ID_SAVE, "&Save", "", self.OnSave),
             (wx.ID_SAVEAS, "Save &As", "", self.OnSaveAs),
             (None, None, None, None),
             (wx.ID_EXIT, "E&xit", "", self.OnExit)]:
            if _id == None:
                menu.AppendSeparator()
            else:
                item = menu.Append(_id, label, helpText)
                self.Bind(wx.EVT_MENU, handler, item)
        bar = wx.MenuBar()
        bar.Append(menu, "&File") 
        self.SetMenuBar(bar)

    def SetTitle(self):
        """Set the title of this window to the name of the file
        being edited."""
        
        super(EditorFrame, self).SetTitle("Editor: %s" % self.filename)

    def defaultFileDialogOptions(self):
        """Return a dictionary with file dialog options that can be
        used in both the save file dialog as well as in the open
        file dialog."""
        
        return dict(message="Choose a file", defaultDir=self.dirname, 
                    wildcard=self.wildcard)

    def askUserForFilename(self, set_title=True, **dialogOptions):
        """Open the file dialog to let the user set a file name."""
        
        dialog = wx.FileDialog(self, **dialogOptions)
        if dialog.ShowModal() == wx.ID_OK:
            userProvidedFilename = True
            self.filename = dialog.GetFilename()
            self.dirname = dialog.GetDirectory()
            if set_title:
                self.SetTitle() # Update window title with new filename
        else:
            userProvidedFilename = False
        dialog.Destroy()
        return userProvidedFilename

    def OnExit(self, event):
        """Hides this window."""
        
        self.Hide()

    def OnSave(self, event, validate=True):
        """Saves the text control content to a file. If validate is set to
        True, check for errors before saving.
        """
        
        if validate:
            if self.validateText():
                self.saveToFile()
        else:
            self.saveToFile()

    def OnOpen(self, event):
        """Show a dialog to let the user open an existing file."""
        
        if self.askUserForFilename(style=wx.OPEN, 
                **self.defaultFileDialogOptions()):
            textfile = open(os.path.join(self.dirname, self.filename), 'r')
            self.text_control.SetValue(textfile.read())
            textfile.close()

    def OnSaveAs(self, event):
        """Save a copy of the current file being edited. Check for errors 
        before saving.
        """
        
        if self.validateText():
            if self.askUserForFilename(defaultFile=self.filename, 
                    style=wx.SAVE, **self.defaultFileDialogOptions()):
                self.OnSave(event, False)
        
    def validateText(self):
        """Check if the content of the text control has sintax errors.
        Subclass must implement this logic.
        """
        
        return False

    
    def saveToFile(self):
        """Saves the content of the text control to the given file name."""
        
        textfile = open(os.path.join(self.dirname, self.filename), 'w')
        textfile.write(self.text_control.GetValue())
        textfile.close()

class AlphabetEditorFrame(EditorFrame):
    
    def __init__(self):
        super(AlphabetEditorFrame, self).__init__(filename="new", 
                                                  extension="alp")

    def CreateInteriorWindowComponents(self):
        """Creates a simple text control that will hold de edited text."""
        
        tc = wx.TextCtrl(self, style=wx.TE_MULTILINE)
        self.text_control = tc
        
    def validateText(self):
        """Every character in the input text should be unique."""
        
        text = self.text_control.GetValue()
        chars = []
        errors = []
        index = 0
        reserved = "|.*+?()[]=;"
        
        # Check if char is repited or reserver.
        for char in text:
            if char in chars:
                errors.append((char, index, 'repited'))
            if char in reserved:
                errors.append((char, index, 'reserved'))
            chars.append(char)
            index += 1
        
        # Show all errors.
        if len(errors) > 0:
            err = ""
            for char, index, error in errors:
                err += "char '" + char + "' at position " + str(index) + \
                    ' is ' + error + "\n"
            dlg = wx.MessageDialog(self, err, 'Errors in the alphabet', 
                                   wx.OK | wx.ICON_WARNING)
            dlg.ShowModal()
            dlg.Destroy()
            return False
        
        return True
        
class DefinitionEditorFrame(EditorFrame):
    
    def __init__(self):
        super(DefinitionEditorFrame, self).__init__(filename="new",
                                                    extension="def")

    def CreateInteriorWindowComponents(self):
        """Creates a panel with a text control to show the alphabet being used,
        a button that shows a file dialog to select the alphabet and a text 
        control to edit the regular definition.
        """
        
        panel = wx.Panel(self)
        
        # Show selected alphabet
        st = wx.StaticText(panel, label='Alphabet: ')
        tc = wx.TextCtrl(panel)
        tc.SetEditable(False)
        
        # Save reference to alphabet text control
        self.text_control_alp = tc
        
        # Show button to select alphabet
        btn = wx.Button(panel, label='Select Alphabet')
        self.Bind(wx.EVT_BUTTON, self.OnSelectAlphabet, btn)

        # Layout in horizontal the static text, text control and button
        hbox = wx.BoxSizer(wx.HORIZONTAL)
        hbox.Add(st, flag=wx.TOP|wx.RIGHT, border=4)
        hbox.Add(tc, proportion=1, flag=wx.RIGHT, border=4)
        hbox.Add(btn)
        
        # Layout in vertical
        vbox = wx.BoxSizer(wx.VERTICAL)
        vbox.Add(hbox, flag=wx.EXPAND|wx.LEFT|wx.RIGHT|wx.TOP, border=10)
        vbox.Add((-1, 5))

        # Text control for editing
        tc = wx.TextCtrl(panel, style=wx.TE_MULTILINE)
        
        # Fill horizontal space
        hbox = wx.BoxSizer(wx.HORIZONTAL)
        hbox.Add(tc, proportion=1, flag=wx.EXPAND)
        
        # Add to vertical layout
        vbox.Add(hbox, proportion=1, flag=wx.LEFT|wx.RIGHT|wx.EXPAND, 
                 border=10)
        
        panel.SetSizer(vbox)
        
        # Save reference to edit text control
        self.text_control = tc

    def OnSelectAlphabet(self, event):
        """Show a dialog to let the user open an existing alphabet file."""
        
        if self.askUserForFilename(set_title=False, style=wx.OPEN, 
                                   message="Choose a file", 
                                   defaultDir=self.dirname, wildcard="*.alp"):
            textfile = open(os.path.join(self.dirname, self.filename), 'r')
            self.text_control_alp.SetValue(textfile.read())
            textfile.close()
            
    def OnOpen(self, event):
        """Show a dialog to let the user open an existing definition file."""
        
        if self.askUserForFilename(style=wx.OPEN, 
                **self.defaultFileDialogOptions()):
            
            textfile = open(os.path.join(self.dirname, self.filename), 'r')
            text = textfile.read()
            
            # Get the alphabet and the definition from file
            alphabet, sep, definition = text.partition(";;")
            id, sep, alph = alphabet.partition(":=")
            id, sep, defi = definition.partition(":=")
            
            # Bind to text controls
            self.text_control_alp.SetValue(alph)
            self.text_control.SetValue(defi)
            
            textfile.close()
    
    def validateText(self):
        """Validates that an alphabet was selected and the tries to parse the 
        definition."""
        
        # Validate the alphabet
        if len(self.text_control_alp.GetValue()) == 0:
            dlg = wx.MessageDialog(self, "No alphabet was selected.", 
                                   'Error', wx.OK | wx.ICON_WARNING)
            dlg.ShowModal()
            dlg.Destroy()
            return False
        
        # Get the alphabet
        alp = []
        for char in self.text_control_alp.GetValue():
            alp.append(char)
        
        # Parse the expression
        t = DefinitionToRegex(self.text_control.GetValue(), alp)
        t.traslate()
        
        # Show errors
        if len(t.errors) > 0:
            err = ""
            for error in t.errors:
                err += error + "\n"
            dlg = wx.MessageDialog(self, err, 
                                   'Errors in the definition', 
                                   wx.OK | wx.ICON_WARNING)
            dlg.ShowModal()
            dlg.Destroy()
            return False
        
        return True
    
    def saveToFile(self):
        """Saves the content of the text control to the given file name."""
        
        textfile = open(os.path.join(self.dirname, self.filename), 'w')
        
        # Save the file with a special format
        output = "alphabet:=" + self.text_control_alp.GetValue() + ";;" + \
                    "definition:=" + self.text_control.GetValue()
        textfile.write(output)
        textfile.close()